using System;
using System.Data;
using Crowler.Model.CoreComponents.DataAccess;
using NHibernate;

namespace CoreComponents.DataAccess
{
    public class UnitOfWork : IUnitOfWork
    {
        private const string TransactionsIsAlreadyOpenExceptionMessage = "The transactions is already open. Close before beginning new one";
        private readonly IUnitOfWorkFactory _unitOfWorkFactory;
        private readonly ISession _session;
        private readonly IsolationLevel _isolationLevel = IsolationLevel.ReadCommitted;
        private IGenericTransaction _currentTransaction;
        public bool IsExecutionException { set; private get; }
        public UnitOfWork(IUnitOfWorkFactory unitOfWorkFactory, ISession session)
        {
            _unitOfWorkFactory = unitOfWorkFactory;
            _session = session;
            BeginTransaction();
        }

        public UnitOfWork(IUnitOfWorkFactory unitOfWorkFactory, ISession session, IsolationLevel isolationLevel)
        {
            _unitOfWorkFactory = unitOfWorkFactory;
            _session = session;
            _isolationLevel = isolationLevel;
            BeginTransaction();
        }

        public void Dispose()
        {
            CompleteTransaction();
            
            _session.Dispose();
            _unitOfWorkFactory.DisposeUnitOfWork(this);
        }

        private void CompleteTransaction()
        {
            if (IsExecutionException)
            {
                RollbackTransaction();
            }else
            {
                CommitTransaction();
            }

        }

        private void RollbackTransaction()
        {
            try
            {
                _currentTransaction.Rollback();
            }
            finally
            {
                _currentTransaction.Dispose();
            }
        }

        public void BeginTransaction()
        {
            if (IsInActiveTransaction)
                throw new InvalidOperationException(TransactionsIsAlreadyOpenExceptionMessage);
            var transaction = _session.BeginTransaction(_isolationLevel);
            _currentTransaction = new GenericTransaction(transaction);
        }

        public void CommitTransaction()
        {
            try
            {
                _currentTransaction.Commit();
            }
            catch
            {
                _currentTransaction.Rollback();
                throw;
            }
            finally
            {
                _currentTransaction.Dispose();
            }
        }

        public bool IsInActiveTransaction
        {
            get
            {
                return _session.Transaction.IsActive;
            }
        }

        public IUnitOfWorkFactory Factory
        {
            get { return _unitOfWorkFactory; }
        }

        public ISession Session
        {
            get { return _session; }
        }

        public void Flush()
        {
            _session.Flush();
        }
    }
}